package com.malie.myspringboot.jdk8;

import com.malie.myspringboot.common.UserEntity;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Optional;


public class OptionalDemo {

    private static final Logger logger = LoggerFactory.getLogger(OptionalDemo.class);

    /**
     * 创建 Optional  实例
     */
    @Test
    public void createOptional() {
        Optional<UserEntity> empty = Optional.empty();
        // NoSuchElementException。
        // empty.get();
    }

    /**
     * of() 和 ofNullable() 方法创建包含值的 Optional。
     * 两个方法的不同之处在于如果你把 null 值作为参数传递进去，of() 方法会抛出 NullPointerException：
     * 如果对象即可能是 null 也可能是非 null，你就应该使用 ofNullable()
     */
    @Test
    public void ofAndOfnullable() {
        UserEntity userEntity = null;
        //  Optional.of(userEntity);      java.lang.NullPointerException
        Optional<UserEntity> entity = Optional.ofNullable(userEntity);
        System.out.println(entity);
    }

    /**
     * 访问 Optional 对象的值
     * 从 Optional 实例中取回实际值对象的方法之一是使用 get() 方法
     */
    @Test
    public void whenCreateOfNullableOptional_thenOk() {
        String name = "John";
        Optional<String> opt = Optional.ofNullable(name);

        Assert.assertEquals("John", opt.get());
    }

    /**
     * get()方法会在值为 null 的时候抛出异常。
     * ①要避免异常，你可以选择首先验证是否有值  isPresent() 为true 则有值
     * ②检查是否有值的另一个选择是 ifPresent() 方法。
     * 该方法除了执行检查，还接受一个Consumer(消费者) 参数，如果对象不是空的，就对执行传入的 Lambda 表达式
     */
    @Test
    public void checkNull() {
        UserEntity userEntity = new UserEntity(18, "huangmalie");
        Optional<UserEntity> optional = Optional.ofNullable(userEntity);
        Assert.assertTrue(optional.isPresent());
        Assert.assertEquals(userEntity.getId(), optional.get().getId());

        optional.ifPresent(e -> Assert.assertEquals(userEntity.getId(), e.getId()));
    }

    /**
     * 返回默认值
     * Optional 类提供了 API 用以返回对象值，或者在对象为空的时候返回默认值。
     * 第一个方法是 orElse()，如果有值则返回该值，否则返回传递给它的参数值    如果对象的初始值不是 null，那么默认值会被忽略
     * 第二个同类型的 API 是 orElseGet() —— 其行为略有不同。这个方法会在有值的时候返回值，如果没有值，
     * 它会执行作为参数传入的 Supplier(供应者) 函数式接口，并将返回其执行结果
     */
    @Test
    public void checkReturn() {
        UserEntity userEntity = null;
        UserEntity userEntity1 = new UserEntity(18, "huangmalie");
        UserEntity entity = Optional.ofNullable(userEntity).orElse(userEntity1);
        Assert.assertEquals(userEntity1.getId(), entity.getId());

        UserEntity elseGet = Optional.ofNullable(userEntity).orElseGet(() -> userEntity1);
        Assert.assertEquals(userEntity1.getId(), elseGet.getId());
    }

    /**
     * orElse() 和 orElseGet() 的不同之处
     * 当对象为空而返回默认对象时，行为并无差异。
     * 当对象不为空 orElse()方法仍然创建了 User 对象。与之相反，orElseGet() 方法不创建 User 对象。
     */
    @Test
    public void givenPresentValue_whenCompare_thenOk() {
        UserEntity user = new UserEntity(14, "1234");
        logger.info("Using orElse");
        UserEntity result = Optional.ofNullable(user).orElse(createNewUser());
        logger.info("Using orElseGet");
        UserEntity result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
    }

    private UserEntity createNewUser() {
        logger.debug("Creating New User");
        return new UserEntity(13, "1234");
    }


    /**
     * 返回异常
     * orElseThrow() API —— 它会在对象为空的时候抛出异常，而不是返回备选的值：
     */
    @Test(expected = IllegalArgumentException.class)
    public void returnThrowException() {
        UserEntity userEntity = null;
        Optional.ofNullable(userEntity).orElseThrow(() -> new IllegalArgumentException());
    }

    /**
     * 转换值
     * map() 和 flatMap()
     */
    @Test
    public void changeValue() {
        UserEntity userEntity = new UserEntity(13, "小张");
        String name = Optional.ofNullable(userEntity).map(e -> e.getName()).orElse("小黄");
        Assert.assertEquals(name, userEntity.getName());
    }

    /**
     * 过滤值
     */
    @Test
    public void filter() {
        UserEntity userEntity = new UserEntity(13, "小张");
        Optional<UserEntity> entity = Optional.ofNullable(userEntity).filter(e -> e.getName() != null);
        Assert.assertTrue(entity.isPresent());

    }

    @Test
    public void test() {
        UserEntity userEntity = null;
        // Integer id = Optional.ofNullable(userEntity.getId()).orElse(1);
        Integer id = Optional.ofNullable(userEntity).map(e -> e.getId()).orElse(1);
        // Integer id = Optional.ofNullable(userEntity).flatMap(e -> e.getId()).orElse(1);
        System.out.println(id);

        System.out.println(Optional.ofNullable(userEntity).isPresent() ? "1" : "2");
    }
}
